#include "stdafx.h"
#include "Lock.h"
#include "WrapperManager.h"
#include "SingleObjWrapper.h"
#include "SmartPtrContext.h"
#include <algorithm>

CWrapperManager* CWrapperManager::m_sWrapperManager = NULL;
int CWrapperManager::extraSize = -1;

CWrapperManager* CWrapperManager::GetInstance()
{
	if (m_sWrapperManager == NULL)
	{
		m_sWrapperManager = new CWrapperManager;
	}
	return m_sWrapperManager;
}

void CWrapperManager::Destroy()
{
	DELETE_IF_NOT_NULL(m_sWrapperManager);
}

size_t CWrapperManager::BlockSize(void *p)
{
	//when allocate an array in windows with MSVC, address returned by new is the address returned
	//by malloc +4;, so, if pass this address to _msize will cause an error
	if ((int)p%8)
	{
		return _msize((char*)p-4)-4;
	}
	return _msize(p);
}

CWrapperManager::CWrapperManager()
{
	Lock l;
	if(extraSize < 0)
	{
		int *p = new int;
		extraSize = BlockSize(p) - sizeof(int);//Windows,0; Linux, 4
		DELETE_IF_NOT_NULL(p);
	}
	m_pNullWrapper = new CSingleObjWrapper(NULL, NULL, 0);
}

CWrapperManager::~CWrapperManager()
{
	Lock l;

	CSmartPtrContext::SetIsCollecting(true);

	for(WrapperMap::iterator i = m_wrappers.begin(); i != m_wrappers.end(); i++)
	{
		CObjectWrapper* temp = static_cast<CSingleObjWrapper*>(i->second);
		DELETE_IF_NOT_NULL(temp);
	}

	m_wrappers.clear();
	
	DELETE_IF_NOT_NULL(m_pNullWrapper);
	
	CSmartPtrContext::SetIsCollecting(false);
}

void CWrapperManager::Insert(void* pRawPointer, CObjectWrapper* pWrapper)
{
	if (pWrapper == NULL || pWrapper == this->m_pNullWrapper)
	{
		return;
	}
	
	m_wrappers.insert(WrapperMap::value_type((long)pRawPointer, pWrapper));
}

// remove the specified ObjectWrapper instance from the list and delete it
void CWrapperManager::Remove(CObjectWrapper* pWrapper)
{
	Lock l;
	
	if(!pWrapper || pWrapper == m_pNullWrapper)	
	{
		// for the NULL Object is not pushed into the set, it is just a single object
		return;
	}

	if(CSmartPtrContext::IsCollecting() == true)
	{
		// TODO: IMPORTANT:
		// here to protect the cycle reference to cause double delete.
		// if this is removed in GC process (rather than CSmartPtr de-construction) or ~CWrapperManager
		// it directly remove the CObjectWrapper object, but not minus ref count and then delete
		// when removing COjectWrapper with inner CSmartPtr, and the ref count of the inner COjbectWrapper
		// of CSmartPtr is 1, and if not this protection, a double deletion will cause.
		// i.e case is GcToolTests::TestMark_CyclePoints.
		return;
	}
	
	m_wrappers.erase((long)(pWrapper->GetRawPointer()));

	DELETE_IF_NOT_NULL(pWrapper);
}

CObjectWrapper* CWrapperManager::IsInList( void* ptr)
{
	WrapperMap::iterator it = m_wrappers.find((long)ptr);
	if ( it != m_wrappers.end() )
	{
		return it->second;
	}
	else
	{
		return NULL;
	}
}

// return -1 if no found, not -1 is the index
int CWrapperManager::IsInObjectScope( void* ptr )
{
	int index = -1;
	int count = 0;

	for (WrapperMap::const_iterator i = m_wrappers.begin(); i != m_wrappers.end(); i++)
	{
		count++;
		unsigned char* baseAddr = (unsigned char*)( i->second->GetRawPointer() );
		size_t scope = i->second->GetSize();
		if (baseAddr <= (unsigned char*)ptr && (unsigned char*)ptr <= baseAddr + scope)
		{
			index = count;
			break;
		}
	}

	return index;
}

// return the ref count of the pWrapper
int CWrapperManager::GetRefCount(void* p)
{
	CObjectWrapper *pTemp = IsInList(p);
	if( NULL == pTemp )
	{
		return 0;
	}

	// the pWrapper is in the list
	return pTemp->GetRefCount();
}

// return the objects that this manager in charge with
int CWrapperManager::GetObjectCount()
{
	return m_wrappers.size();
}

// decrease the ref count of the CObjectWrapper that the ptr is in,
// if the ref count is 0, remove it from the list
void CWrapperManager::Release(void* ptr)
{
	CObjectWrapper * toRelease = IsInList(ptr);
	// the ptr must be in the list

	if (toRelease)
	{
		// decease the ref count
		toRelease->Release();

		// check whether it is necessary to remove it from the managed list
		if ( toRelease->GetRefCount() == 0 )
		{
			Remove(toRelease);
		}
	}
}

/**
* return true if the ptr itself is located in the inner of other object managed by GC, or pointer to NULL.
* The principle behind that is the CSmartPtr object is also wrappered as the CObjectWrapper.
* besides it take the advantage of the syntax. all the embedded pointers will be assigned NULL
* when it is declared, which will not be put into the WrapperManager.
* eg.
* struct XXX
* {
*    CSmartPtr<int> ptr1;
* }
* or
* class XXX
* {
*    CSmartPtr<int> ptr1;
* }
*/

bool CWrapperManager::IsEmbeddedPtr( void *ptr )
{
	// if the ptr is the embedded one, it will not in the m_wrapper
	return IsInObjectScope(ptr) != -1;
}

void CWrapperManager::SetPtrInUse( void* ptr, bool bInUse )
{
	// skip NULL
	if (ptr == NULL)
	{
		return;
	}

	CObjectWrapper *temp = IsInList(ptr);
	temp->SetInUser(bInUse);
}

size_t CWrapperManager::GetSize( void* ptr )
{
	size_t returnSize = 0;
	CObjectWrapper *temp = IsInList(ptr);
	
	return temp->GetSize();
}

bool CWrapperManager::IsPtrInUse( void* ptr )
{
	bool bInUse = false;

	if (ptr == NULL)
	{
		// NULL should always be in use
		return true;
	}

	CObjectWrapper *temp = IsInList(ptr);
	return temp->IsInUse();
}
